package drds.configuration.db.xml_mapping;

import drds.configuration.db.test.object_column_mapping.mapper.UserMapper;
import drds.configuration.db.xml_mapping.annotation.dto.Dto;
import drds.configuration.db.xml_mapping.annotation.dto.DtoMapping;
import drds.configuration.db.xml_mapping.annotation.mapper_class.Database;
import drds.configuration.db.xml_mapping.annotation.mapper_class.DatabaseId;
import drds.configuration.db.xml_mapping.annotation.mapper_class.Mapper;
import drds.configuration.db.xml_mapping.configuration.Configuration;
import drds.configuration.db.xml_mapping.configuration.ParameterMap;
import drds.configuration.db.xml_mapping.configuration.ResultMap;
import drds.configuration.db.xml_mapping.configuration.XmlMappersBuilder;
import drds.configuration.db.xml_mapping.sql_execute.MapperExecutionContext;
import drds.configuration.db.xml_mapping.sql_execute.MapperExecutionContextImpl;
import drds.tools.$;
import drds.tools.ClassSearcher;
import drds.tools.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

//@Slf4j
public class MapperFactory {
    public static Logger log = LoggerFactory.getLogger(MapperFactory.class);

    /**
     * dto/入参和出参配置
     */
    public static void dtoClassScan() {
        List<Class> classList = ClassSearcher.findAnnotationClass(Dto.class, $.rootPackageName);
        if (!$.isNotNullAndHasElement(classList)) {
            log.warn("没有注解标识的类");
            return;
        }
        for (Class clazz : classList) {
            DtoMapping dtoMapping = new DtoMapping(clazz);
            //这个是最原始的DTO信息注册
            //在xml中可以对这部分进行重写,两者共用相关的dto类
            dtoMapping.buildFeildToColumnMapping();
            DtoClassMapping.classToDtoMappingMap.put(clazz, dtoMapping);
        }
    }

    //
    public static void mapperScan() {
        /**
         * 该数据库交互框架只是为drds服务,故代码写成这样。
         */
        List<Class> classList = ClassSearcher.findImplementionClass(Database.class, $.rootPackageName);
        if (!$.isNotNullAndHasElement(classList)) {
            log.info("没有注解标识的类");
            return;
        }
        Map<String, String> mapperDatabaseIdToPackageNameMap = new HashMap<String, String>();
        for (Class clazz : classList) {
            DatabaseId databaseId = (DatabaseId) clazz.getAnnotation(DatabaseId.class);
            String format = String.format("请标记是哪个组数据库(%s)", clazz);
            if (databaseId == null) {
                throw new IllegalStateException(format);
            }
            if (databaseId.databaseId() == null) {
                throw new IllegalStateException(format);
            }
            mapperDatabaseIdToPackageNameMap.put(databaseId.databaseId(), clazz.getPackage().getName());
            //在组数据库里面检查是否存在
            format = String.format("包%s标记为%s组数据库", clazz.getPackage().getName(), databaseId.databaseId());
            log.info(format);
        }
        //
        for (Map.Entry entry : mapperDatabaseIdToPackageNameMap.entrySet()) {
            String mapperDatabaseId = (String) entry.getKey();
            String packageName = (String) entry.getValue();
            //
            doMapperScan(mapperDatabaseId, packageName);
        }
    }

    /**
     * @param mapperDatabaseId
     * @param basePackage      业务所在的基础包
     */
    private static void doMapperScan(String mapperDatabaseId, String basePackage) {
        List<Class> classList = ClassSearcher.findAnnotationClass(Mapper.class, basePackage);
        if (!$.isNotNullAndHasElement(classList)) {
            log.warn("没有注解标识的类");
            return;
        }
        ConfigurationContext.databaseIdToConfigurationMap.putIfAbsent(mapperDatabaseId, new Configuration());
        Configuration configuration = ConfigurationContext.databaseIdToConfigurationMap.get(mapperDatabaseId);
        configuration.setMapperDatabaseId(mapperDatabaseId);
        for (Class clazz : classList) {
            configuration.addMapperInterfaceClass(clazz);
            ConfigurationContext.mapperClassToDatabaseIdMap.put(clazz, mapperDatabaseId);//快速查找数据源
        }
        mapperElement(configuration, classList);
    }

    private static void mapperElement(Configuration configuration, List<Class> classList) {
        if ($.isNotNullAndHasElement(classList)) {
            for (Class clazz : classList) {
                String classFullClassName = clazz.getName();
                if (Constants.developMode) {
                    log.debug("对类:" + classFullClassName + "对应的xml mapper文件进行解析");
                }
                //String path = clazz.getPackage().getName().replace(".","/")+"/xml/" + clazz.getSimpleName() + ".xml";
                //InputStream inputStream = clazz.getResourceAsStream(path);
                String path = clazz.getPackage().getName().replace(".", "/") + "/xml/" + clazz.getSimpleName() + ".xml";
                InputStream inputStream = clazz.getClassLoader().getResourceAsStream(path);
                XmlMappersBuilder xmlMappersBuilder = new XmlMappersBuilder(inputStream, configuration, classFullClassName);
                xmlMappersBuilder.parse();
            }
        }
    }

    //
    public static UserMapper getMapper(Class clazz) {
        String mapperDatabaseId = ConfigurationContext.mapperClassToDatabaseIdMap.get(clazz);
        if ($.isNullOrEmpty(mapperDatabaseId)) {
            throw new NullPointerException("mapperDatabaseId");
        }
        Configuration configuration = ConfigurationContext.databaseIdToConfigurationMap.get(mapperDatabaseId);
        MapperExecutionContext mapperExecutionContext = new MapperExecutionContextImpl(configuration);
        return mapperExecutionContext.getMapper(UserMapper.class);
    }

    public static class DtoClassMapping {
        public static final Map<Class, DtoMapping> classToDtoMappingMap = new HashMap<Class, DtoMapping>();
        public static final Map<String, ParameterMap> parameterMapIdToParameterMapMap = new HashMap<String, ParameterMap>();
        public static final Map<String, ResultMap> resultMapIdToResultMapMap = new HashMap<String, ResultMap>();
    }

    public static class ConfigurationContext {
        public static ConcurrentHashMap<String, Configuration> databaseIdToConfigurationMap = new ConcurrentHashMap<String, Configuration>();
        public static ConcurrentHashMap<Class, String> mapperClassToDatabaseIdMap = new ConcurrentHashMap<Class, String>();

    }
}
